package audio.output;
import java.io.File;
import java.io.IOException;
import javax.sound.sampled.AudioFileFormat;
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import org.trianacode.taskgraph.Unit;
import org.tritonus.share.sampled.AudioSystemShadow;
import org.tritonus.share.sampled.file.AudioOutputStream;
import org.tritonus.share.sampled.file.TDataOutputStream;
import triana.types.SampleSet;
import triana.types.audio.AudioUtils;
import triana.types.audio.MultipleAudio;
/**
* A Unit class to support the writing/saving of (chunked/nonchunked) audio. A seperate thread is used for the actual
* writing, to boost CPU efficiency, which the toByteArray method converts the audio from a short array to a byte array
* in ths current thread/class. This requires the use of Tritonus jars, which are now included as part of the Triana
* distribution.
* <p/>
* Created by Eddie Al-Shakarchi Contact e.alshakarchi@cs.cf.ac.uk
*
* @author Eddie Al-Shakarchi
* @version $Revision: 4052 $
*/
public class WriteAiff_old extends Unit {
public static final long filt = (long) (Math.pow(2.0, 8) - 1);
static boolean bigendian = true;
static {
try {
String os = System.getProperty("os.name");
if (os.startsWith("Windows")) {
bigendian = false;
}
} catch (Exception ee) {
bigendian = false;
}
}
int oldBufferSize = 0;
int outputBufferSize;
byte[] bytedata;
//TargetDataLine outputChannel = null; // This is initialised for the DataLine.Info line later
AudioFormat format = null;
AudioFormat newformat = null;
AudioFormat outputFormat = null;
AudioWriter audioWriter = null;
AudioInputStream audioInputStream;
AudioFileFormat.Type audioFileFormat;
File outputFile;
String formatType;
String fileName = "";
String fileName2;
byte[] bytes;
TDataOutputStream dataOutputStream = null;
AudioOutputStream audioOutputStream = null;
/**
* **************************************************************************************** This is the main
* processing method. Called whenever there is data for the unit to process *****************************************************************************************
*/
public void process() throws Exception {
Object in = getInputAtNode(0);
MultipleAudio input = (MultipleAudio) in;
// SampleSet
if (in instanceof SampleSet) {
SampleSet input2 = (SampleSet) in;
newformat = new AudioFormat(AudioFormat.Encoding.PCM_SIGNED, (float) input2.getSamplingRate(),
16, 1, 2, (int) input2.getSamplingRate(), bigendian);
bytes = AudioUtils.to16BitByteArray(input2, newformat);
}
// A MultipleAudio type
else {
newformat = input.getAudioFormat();
AudioFormat au;
// For each channel
for (int chan = 0; chan < newformat.getChannels(); ++chan) {
au = input.getAudioFormat(); // grab the audio format
if (!newformat.matches(au)) {
throw new Exception("Incompatible Format Error in " + getToolName() +
"\n : Format at node 0 is : \n" + newformat + "\n and format at node "
+ String.valueOf(chan) + " is : \n" + au);
}
}
// convert to byte array in correct format
try {
bytes = toByteArray(input); // calls toByteArray method on 'input'
if (newformat.getSampleSizeInBits() == 8) {
outputBufferSize = input.getChannelLength(0);
} else if (newformat.getSampleSizeInBits() == 16) {
outputBufferSize = input.getChannelLength(0) * 4;
}
}
catch (Exception e) {
e.printStackTrace();
throw new Exception("Incompatible Data Error in " + getToolName());
}
}
if (audioWriter == null) {
setUpWriter(newformat); // can only set one format per output line
format = newformat;
audioWriter = new AudioWriter(audioOutputStream);
audioWriter.addChunk(bytes);
audioWriter.start();
} else {
audioWriter.addChunk(bytes);
}
output(in); // output the input data if there are any output nodes
}
/* public void setUpGUI(){
JFileChooser chooser = new JFileChooser();
chooser.setDialogTitle("Save Audio to File");
if (chooser.showOpenDialog(null) == JFileChooser.APPROVE_OPTION) {
String fn = chooser.getSelectedFile().getAbsolutePath();
userScreen(chooser.getSelectedFile().getName());
updateParameter("fileName", fn);
lastDir = chooser.getSelectedFile().getPath();
}
}
*/
/**
* **************************************************************************************** This method sets up the
* output stream, and is only called IF there is a change in the * format of the incoming audio. The
* dataOutputStream is started - it's written to on the * fly in the AudioWriter class *
* ****************************************************************************************
*/
public void setUpWriter(AudioFormat audioFormat) {
System.out.println("Setting Up Writer...");
if (fileName.endsWith(".aiff") || fileName.endsWith(".aif")) { //(formatType = WAV)){
audioFileFormat = AudioFileFormat.Type.AIFF;
fileName2 = fileName;
} else {
audioFileFormat = AudioFileFormat.Type.AIFF;
fileName2 = fileName + ".aiff";
}
outputFile = new File(fileName2);
//Creates new audio format object
outputFormat = new AudioFormat(audioFormat.getEncoding(), audioFormat.getSampleRate(),
audioFormat.getSampleSizeInBits(), audioFormat.getChannels(),
audioFormat.getFrameSize(), audioFormat.getFrameRate(),
audioFormat.isBigEndian());
System.out.println("In WRITE : Format.. " + outputFormat);
System.out.println("Frame size = " + outputFormat.getFrameSize());
System.out.println("Frame Rate = " + outputFormat.getFrameRate());
long lLengthInBytes = AudioSystem.NOT_SPECIFIED;
try {
dataOutputStream = AudioSystemShadow.getDataOutputStream(outputFile);
}
catch (IOException e) {
e.printStackTrace();
}
audioFileFormat = AudioFileFormat.Type.AIFF;
audioOutputStream = AudioSystemShadow.getAudioOutputStream(audioFileFormat, outputFormat,
lLengthInBytes, dataOutputStream);
// This uses the DataLine.Info subclass to obtain and open a target data line
// DataLine.Info info = new DataLine.Info(TargetDataLine.class, outputFormat);
// try {
// outputChannel = ((TargetDataLine) AudioSystem.getLine(info));
// outputChannel.open(outputFormat);
// outputChannel.start();
// }
// catch (LineUnavailableException e) {
// System.err.println("ERROR!! line not supported : " + audioFormat);
// }
}
/**
* ************************************************************************ This method converts the multiple audio
* input to a specific byte array,* taking into account the bit depth of the audio file. *
* ************************************************************************
*/
public byte[] toByteArray(MultipleAudio input) throws Exception {
int frameSizeInBytes = newformat.getFrameSize();
int channels = newformat.getChannels();
int chan;
int samples = 0;
int pos;
int i, j;
for (chan = 0; chan < channels; ++chan) {
if (samples < input.getChannelLength(chan)) {
samples = input.getChannelLength(chan);
}
}
samples = samples * channels;
if (frameSizeInBytes == 2) {
bytedata = new byte[(frameSizeInBytes * samples)];
} else {
bytedata = new byte[(frameSizeInBytes * samples) / 2];
}
// For each channel... basically this converts from multiple audio to byte array
for (chan = 0; chan < channels; ++chan) {
Object o = input.getChannel(chan);
if (newformat.getSampleSizeInBits() == 8) {
byte channel[] = (byte[]) o;
for (i = 0; i < channel.length; ++i) {
bytedata[chan + (i * channels)] = channel[i];
}
} else if (newformat.getSampleSizeInBits() == 16) {
int b = 2;
short bitVal;
short vals[] = (short[]) o;
if (bigendian) {
for (j = 0; j < vals.length; ++j) {
pos = j * channels;
bitVal = (short) vals[j];
for (i = 0; i < b; ++i) {
bytedata[(chan * b) + (pos * b) + (b - i - 1)] = (byte) (bitVal & filt);
bitVal = (short) (bitVal >> 8);
}
}
} else { // a windows machine ... need to swap the bytes back for the audio engine
for (j = 0; j < vals.length; ++j) {
pos = j * channels;
bitVal = (short) vals[j];
for (i = 0; i < b; ++i) {
bytedata[(chan * b) + (pos * b) + i] = (byte) (bitVal & filt);
bitVal = (short) (bitVal >> 8);
}
}
}
} else { // assume 32 bit
int b = 4;
int bitVal;
int vals[] = (int[]) o;
if (bigendian) {
for (j = 0; j < vals.length; ++j) {
pos = j * channels;
bitVal = (int) vals[j];
for (i = 0; i < b; ++i) {
bytedata[(chan * b) + b - i - 1 + (pos * b)] = (byte) (bitVal & filt);
bitVal = (short) (bitVal >> 8);
}
}
} else { // swap 'em if little endian
for (j = 0; j < vals.length; ++j) {
pos = j * channels;
bitVal = (int) vals[j];
for (i = 0; i < b; ++i) {
bytedata[(chan * b) + i + (pos * b)] = (byte) (bitVal & filt);
bitVal = (short) (bitVal >> 8);
}
}
}
}
}
return bytedata;
}
/**
* Called when the unit is created. Initialises the unit's properties and parameters.
*/
public void init() {
super.init();
// Initialise node properties
setDefaultInputNodes(1);
setMinimumInputNodes(1);
setMaximumInputNodes(1);
setDefaultOutputNodes(1);
setMinimumOutputNodes(0);
setMaximumOutputNodes(Integer.MAX_VALUE);
// Initialise parameter update policy
setParameterUpdatePolicy(IMMEDIATE_UPDATE);
// Initialise pop-up description and help file location
setPopUpDescription("Saves audio as AIFF file");
setHelpFileLocation("WriteAIFF.html");
// Define initial value and type of parameters
defineParameter("fileName", "untitled", USER_ACCESSIBLE);
// Initialise GUI builder interface
String guilines = "";
guilines += "New File Name: $title fileName TextField untitled\n";
setGUIBuilderV2Info(guilines);
}
public void reset() {
// Set unit variables to the values specified by the parameters
fileName = (String) getParameter("fileName");
stopDeMusicMan();
}
// Stops the music using methods from Java Sound API
public void stopDeMusicMan() {
if (audioWriter == null) {
return;
}
audioWriter.stopWriter();
try {
audioOutputStream.close();
}
catch (Exception ee) {
}
audioOutputStream = null;
format = null;
audioWriter = null;
}
/**
* Called when the unit is disposed of.
*/
public void dispose() {
System.out.println("Disposig Audio Writer");
stopDeMusicMan();
}
/**
* Called a parameters is updated (e.g. by the GUI)
*/
public void parameterUpdate(String paramname, Object value) {
if (paramname.equals("fileName")) {
fileName = (String) value;
}
}
/**
* @return an array of the input types for StringGen
*/
public String[] getInputTypes() {
return new String[]{"triana.types.SampleSet", "triana.types.audio.MultipleAudio"};
}
/**
* @return an array of the output types for StringGen
*/
public String[] getOutputTypes() {
return new String[]{"triana.types.SampleSet", "triana.types.audio.MultipleAudio"};
}
}